﻿using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using RanbuUI.Converters;

namespace RanbuUI
{
    /// <summary>
    /// Interaction logic for DataGridEx.xaml
    /// </summary>
    public partial class DataGridEx : UserControl
    {
        public DataGridEx()
        {
            this.InitializeComponent();
        }

        private enum CustomTemplateKind
        {
            FlagsEnumDisplay,

            FlagsEnumEditing
        }

        private static bool IsCompositeFlagEnum(Enum value)
        {
            ulong numeric = Convert.ToUInt64(value);
            while (numeric != 0 && (numeric & 1) == 0)
            {
                numeric >>= 1;
            }

            return numeric > 1;
        }

        private void OnAutoGeneratingColumn(object sender, DataGridAutoGeneratingColumnEventArgs e)
        {
            if (e.PropertyType.IsEnum && Attribute.IsDefined(e.PropertyType, typeof(FlagsAttribute)))
            {
                e.Column = new DataGridTemplateColumn
                {
                    Header = e.PropertyName,
                    CellTemplateSelector =
                        new CustomTemplateSelector(e.PropertyName, e.PropertyType, CustomTemplateKind.FlagsEnumDisplay),
                    CellEditingTemplateSelector =
                        new CustomTemplateSelector(e.PropertyName, e.PropertyType, CustomTemplateKind.FlagsEnumEditing)
                };
            }
        }

        private class CustomTemplateSelector : DataTemplateSelector
        {
            private readonly CustomTemplateKind kind;

            private readonly string propertyName;

            private readonly Type propertyType;

            public CustomTemplateSelector(string name, Type type, CustomTemplateKind kind)
            {
                this.propertyName = name;
                this.propertyType = type;
                this.kind = kind;
            }

            public override DataTemplate SelectTemplate(object item, DependencyObject container)
            {
                switch (this.kind)
                {
                    case CustomTemplateKind.FlagsEnumDisplay:
                        return CreateFlagsEnumDisplayTemplate(this.propertyName);

                    case CustomTemplateKind.FlagsEnumEditing:
                        return CreateFlagsEnumEditingTemplate(item, this.propertyName, this.propertyType);

                    default:
                        throw new NotImplementedException();
                }
            }

            private static DataTemplate CreateFlagsEnumDisplayTemplate(string name)
            {
                DataTemplate template = new DataTemplate();
                template.VisualTree = new FrameworkElementFactory(typeof(Label));
                template.VisualTree.SetBinding(ContentProperty, new Binding(name));
                return template;
            }

            private static DataTemplate CreateFlagsEnumEditingTemplate(object target, string name, Type type)
            {
                var converter = new EnumFlagToBooleanConverter(target, name);

                var stackPanel = new FrameworkElementFactory(typeof(StackPanel));
                var simpleValueGroup = new FrameworkElementFactory(typeof(GroupBox));
                var compositeValueGroup = new FrameworkElementFactory(typeof(GroupBox));

                simpleValueGroup.SetValue(HeaderedContentControl.HeaderProperty, "Flags");
                var simpleValueContainer = new FrameworkElementFactory(typeof(StackPanel));
                simpleValueGroup.AppendChild(simpleValueContainer);

                compositeValueGroup.SetValue(HeaderedContentControl.HeaderProperty, "Presets");
                var compositeValueContainer = new FrameworkElementFactory(typeof(StackPanel));
                compositeValueGroup.AppendChild(compositeValueContainer);

                foreach (var value in Enum.GetValues(type))
                {
                    var checkBox = new FrameworkElementFactory(typeof(CheckBox));
                    checkBox.SetValue(ContentProperty, value);
                    var binding = new Binding(name)
                    {
                        Converter = converter,
                        ConverterParameter = value,
                        UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged
                    };

                    checkBox.SetBinding(ToggleButton.IsCheckedProperty, binding);
                    if (IsCompositeFlagEnum((Enum)value))
                    {
                        compositeValueContainer.AppendChild(checkBox);
                    }
                    else
                    {
                        simpleValueContainer.AppendChild(checkBox);
                        if (Convert.ToUInt64(value) == 0)
                        {
                            simpleValueContainer.AppendChild(new FrameworkElementFactory(typeof(Separator)));
                        }
                    }
                }

                stackPanel.AppendChild(simpleValueGroup);
                if (compositeValueContainer.FirstChild != null)
                {
                    stackPanel.AppendChild(compositeValueGroup);
                }

                return new DataTemplate { VisualTree = stackPanel };
            }
        }
    }
}
